#include "gate_event_handler.h"
#include "gate_client_session.h"
#include "EndianCast.h"

namespace utility
{
namespace network
{
void gate_event_handler::send(gate_client_session * session, void * message, size_t message_length)
{
    size_t sz = message_length + sizeof(gate_protocol_header);
    // allocate the memory from stack
    char * buf = (char *)alloca(sz);
    gate_protocol_header * header = (gate_protocol_header*)buf;
    _construct_header(COMMUNICATION, session, header);
    memcpy(buf + sizeof(gate_protocol_header), message, message_length);
    if (_get_session())
        _get_session()->send(buf, sz);
}

void gate_event_handler::disconnect(gate_client_session * s)
{
    fake_socket socket;
    _session2fakesocket(s, &socket);

    session_container::iterator i = sub_sessions_.find(socket.in_addr.value_);
    if (i != sub_sessions_.end())
    {
        _send_operation(DISCONNECT_NTF, s);
        gate_client_session * session = i->second;
        session_event_handler::pointer handler = session->get_event_handler();
        if (handler)
            handler->on_disconnected();
        sub_sessions_.erase(i);
        _release_client_session(session);
    }
}

void gate_event_handler::_receive(gate_protocol_header * header, const char * message, uint32 message_length)
{
    if (message_length < 0)
        return;

    fake_socket socket;
    _header2fakesocket(header, &socket);

    session_container::iterator i = sub_sessions_.find(socket.in_addr.value_);
    if (i != sub_sessions_.end())
    {
        gate_client_session * s = i->second;
        session_event_handler::pointer handler = s->get_event_handler();
        if (handler)
            handler->on_receive(message, message_length);
    }
}

void gate_event_handler::_ntf_disconnect(gate_protocol_header * header)
{
    fake_socket socket;
    _header2fakesocket(header, &socket);
    session_container::iterator i = sub_sessions_.find(socket.in_addr.value_);
    if (i != sub_sessions_.end())
    {
        gate_client_session * s = i->second;
        session_event_handler::pointer handler = s->get_event_handler();
        if (handler)
            handler->on_disconnected();
        sub_sessions_.erase(i);
        _release_client_session(s);
    }
}

void gate_event_handler::_disconnect_all()
{
    session_container::iterator i = sub_sessions_.begin();
    for (; i != sub_sessions_.end(); i++)
    {
        gate_client_session * s = i->second;
        session_event_handler::pointer handler = s->get_event_handler();
        if (handler)
            handler->on_disconnected();
        _release_client_session(s);
    }
}

void gate_event_handler::_send_operation(uint8 op_type, gate_client_session * s)
{
    gate_protocol_header header;
    _construct_header(op_type, s, &header);

    if (_get_session())
        _get_session()->send(&header, sizeof(header));
}

void gate_event_handler::_construct_header(uint8 op_type, gate_client_session * s, gate_protocol_header * header)
{
    header->op_type = op_type;
    header->source_ip = htonl(s->get_local_ip());
    header->source_port = htons(s->get_local_port());
    header->destination_ip = htonl(s->get_remote_ip());
    header->destination_port = htons(s->get_remote_port());
}

void gate_event_handler::_session2fakesocket(gate_client_session * s, fake_socket * socket)
{
    socket->in_addr.socket_.source_ip_ = ntohl(s->get_remote_ip());
    socket->in_addr.socket_.source_port_ = ntohs(s->get_remote_port());
    socket->in_addr.socket_.local_port_ = ntohs(s->get_local_port());
}

void gate_event_handler::_header2fakesocket(gate_protocol_header * header, fake_socket * socket)
{
    socket->in_addr.socket_.source_ip_ = ntohl(header->source_ip);
    socket->in_addr.socket_.source_port_ = ntohs(header->source_port);
    socket->in_addr.socket_.local_port_ = ntohs(header->destination_port);
}

void gate_event_handler::_release_client_session(gate_client_session * s)
{
    _on_subsession_disconnected(s);
    delete s;
}

}
}